﻿namespace DimensionsHelper.CommandLine.ConsoleTracer;

public abstract class NavigatorLineBase(int line) : INavigatorLine
{
    protected int EndCharacter;
    protected int EndLine = line;
    protected INavigatorRange? Head;
    protected INavigatorLine? Next;
    protected INavigatorLine? Previous;
    protected int StartCharacter;
    protected int StartLine = line;
    protected INavigatorRange? Tail;
    
    public INavigatorRange Append(string content)
    {
        INavigatorRange range = CreateRange(
            Tail,
            Tail == null ? ConsolePosition.Create(EndLine, 0) : Tail.GetLocation().End,
            content);

        range.LocationChanged += (_, e) => { UpdateLocation(e.Location); };

        ConsoleLocation loc = range.GetLocation();
        Head ??= range;
        Tail = range;
        UpdateLocation(loc);
        return range;
    }


    public void Clear()
    {
        ConsoleLocation loc = GetLocation();
        int lines = loc.GetLines();
        int startLine = loc.Start.Line;

        while (lines > 0)
        {
            ConsoleHelper.ClearLine(startLine++);
            lines--;
        }
    }


    public INavigatorRange? First()
    {
        return Head;
    }


    public INavigatorRange? Last()
    {
        return Tail;
    }


    public ConsoleLocation GetLocation()
    {
        return ConsoleLocation.Create(
            ConsolePosition.Create(StartLine, StartCharacter),
            ConsolePosition.Create(EndLine, EndCharacter));
    }


    public INavigatorLine? NextLine()
    {
        return Next;
    }


    public INavigatorLine? PreviousLine()
    {
        return Previous;
    }


    public INavigatorRange? RangeAt(int index)
    {
        INavigatorRange? range = Head;
        index--;

        while (index >= 0 && range != null)
        {
            range = range.Next();
            index--;
        }

        return range;
    }

    public void Refresh()
    {
        ForEachRange(r => r.Refresh());
    }


    public void Shift(int shiftLines)
    {
        if (shiftLines == 0)
        {
            return;
        }

        StartLine += shiftLines;
        EndLine += shiftLines;
        Head?.Shift(shiftLines);
    }


    private void UpdateLocation(ConsoleLocation location)
    {
        if (Head == null)
        {
            StartLine = location.Start.Line;
            StartCharacter = location.Start.Character;
        }

        EndLine = location.End.Line;
        EndCharacter = location.End.Character;
    }

    protected abstract INavigatorRange CreateRange(INavigatorRange? prev, ConsolePosition start, string content);

    public void ForEachRange(Action<INavigatorRange> callback)
    {
        if (Head == null)
        {
            return;
        }

        INavigatorRange? range = Head;
        while (range != null)
        {
            callback(range);
            range = range.Next();
        }
    }
}